热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

发生|时会_[Android基础]WebView

篇首语:本文由编程笔记#小编为大家整理,主要介绍了[Android基础]WebView相关的知识,希望对你有一定的参考价值。[Demo下载]

篇首语:本文由编程笔记#小编为大家整理,主要介绍了[Android基础] WebView相关的知识,希望对你有一定的参考价值。


[ Demo下载 ]


资源
  1. Web Apps
  2. WebView
  3. Android4.4 webview实现分析
  4. Android WebView使用深入浅出
  5. 深入讲解WebView(上) - 互调,缓存,异常处理等
  6. 深入讲解WebView(下) - session,COOKIE等
  7. Android WebView Memory Leak WebView内存泄漏 ==! 这个我用leakcanary没检测出来
  8. PHP、Android、iOS 的恩恩怨怨



android 4.4(KitKat)开始,WebView组件是基于开源的Chromium项目.包含V8 js引擎并支持新的web标准,新webView也共享Chrome for Android的渲染引擎,另外,从5.0(Lollipop)开始,WebView被移到独立的apk中,因此它可以进行单独更新,可以从 “settings – Apps – Android System WebView” 中查看其版本;



用途

默认情况下,webView不启用js交互,并会忽略页面错误,适用于展示静态信息;
也可以启用js功能,实现与用户的交互


辅助类
  • WebChromeClient 当可能影响webView UI的操作发生时会调用到该类,比如进度变化或者js提示框…
  • WebViewClient 当可能影响内容渲染的操作发生时会调用到该类,比如错误等…另外,可以通过重写 shouldOverrideUrlLoading() 来中断url的加载;
  • WebSettings 功能设置,比如可否允许js代码;

基本操作
  • 访问网络的话需要添加网络权限

<uses-permission android:name&#61;"android.permission.INTERNET" />

  • 启用js功能

WebSettings webSettings &#61; myWebView.getSettings();
webSettings.setJavascriptEnabled(true);

设置返回键回退功能

mWv.setOnKeyListener(new View.OnKeyListener()
&#64;Override
public boolean onKey(View v, int keyCode, KeyEvent event)
// 需要添加 mWv.canGoBack(),不然当返回到初始页面时,可能无法继续通过返回键关闭页面
if (keyCode &#61;&#61; KeyEvent.KEYCODE_BACK && mWv.canGoBack())
mWv.goBack();
return true;

return false;

);

也可以通过设置所在Activity的onBackPressed()方法来支持webView回退:

&#64;Override
public void onBackPressed()
if (mWv.canGoBack())
mWv.goBack();
else
super.onBackPressed();


设置标题

mWv.setWebChromeClient(new WebChromeClient()
&#64;Override
public void onReceivedTitle(WebView view, String title)
// title 是获取到的网页title,可以将之设置为webView所在页面的标题
MainActivity.this.setTitle(title);

);

设置加载进度

&#64;Override
protected void onCreate(Bundle savedInstanceState)
//requestWindowFeature(Window.FEATURE_PROGRESS);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//getWindow().setFeatureInt(Window.FEATURE_PROGRESS, Window.PROGRESS_VISIBILITY_ON);
......
mProgressDlg &#61; new ProgressDialog(this);
mProgressDlg.setMessage("loading...");
mWv.setWebChromeClient(new WebChromeClient()
&#64;Override
public void onProgressChanged(WebView view, int newProgress)
//更新进度条示数
//这种方式我没看到效果...
//MainActivity.this.setProgress(newProgress);
//使用控件ProgressDialog来显示进度
//但记得这种方式需要在error发生时也进行取消
if (newProgress <&#61; 90)
mProgressDlg.setProgress(newProgress);
else
mProgressDlg.dismiss();


);
mWv.setWebViewClient(new WebViewClient()
&#64;Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error)
super.onReceivedError(view, request, error);
// 加载某些网站的时候会报:ERR_CONNECTION_REFUSED,因此需要在这里取消进度条的显示
Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
if (mProgressDlg.isShowing())
mProgressDlg.dismiss();


&#64;Override
public void onPageStarted(WebView view, String url, Bitmap favicon)
super.onPageStarted(view, url, favicon);
if (!mProgressDlg.isShowing())
mProgressDlg.show();


&#64;Override
public void onPageFinished(WebView view, String url)
super.onPageFinished(view, url);
if (mProgressDlg.isShowing())
mProgressDlg.dismiss();


);

控制url跳转

mWv.setWebViewClient(new WebViewClient()
&#64;Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
// 这个方法我没有重写的话也还是使用webView来加载链接
// 而且我这里测试返回的true/false貌似没什么影响
if (Uri.parse(url).getHost().endsWith("jianshu.com"))
//若是指定服务器的链接则在当前webView中跳转
view.loadUrl(url);
return false;
else if (Uri.parse(url).getHost().length() &#61;&#61; 0)
// 本地链接的话直接在webView中跳转
return false;

// 其他情况则使用系统浏览器打开网址
Uri uri &#61; Uri.parse(url);
Intent intent &#61; new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
return true;

);

加载页面


1. 加载本地asset文件

mWv.loadUrl("file:///android_asset/index.html");

2. 加载本地网页2

//index.html文件放置于 src/main/assets 目录中
myWebView.loadUrl("file:///android_asset/index.html");

3. 加载网页

myWebView.loadUrl("http://www.jianshu.com/users/302253a7ed00/latest_articles");

4. 解析html字符串

String summary &#61; "\\n" &#43;
"\\n" &#43;
"\\n" &#43;
" \\n" &#43;
" \\n" &#43;
" \\n" &#43;
"\\n" &#43;
"\\n" &#43;
"

\\n" &#43;
" \\n" &#43;
"
\\n" &#43;
"\\n" &#43;
"\\n" &#43;
"\\n" &#43;
"
\\n"
&#43;
"个人主页\\n" &#43;
"\\n" &#43;
"";
// 官网例子给的下面的写法,但是会出现中文乱码,
// 原因:http://blog.csdn.net/top_code/article/details/9163597
// mWv.loadData(summary, "text/html", "utf-8");
mWv.loadData(summary, "text/html;charset&#61;UTF-8", null);

使用android studio的话,项目结构中没有asset目录,需要手动创建 src/main/assets 目录即可;
扩展:
1. 如果html文件存于sdcard&#xff1a;则加前缀&#xff1a;content://com.android.htmlfileprovider/sdcard/
   另外, content 前缀可能导致异常&#xff0c;直接使用 file:///sdcard/ 或者 file:/sdcard 也可以;
2. 也可使用 locaData() ,先将文件读取出来,在传入字符串到方法中,可以用于展示页面,但不会引用css,js等文件;


js与andorid互调


  1. 通过 addJavascriptInterface() 来设置接口,传入实例和类名,让js可以调用;


    Note: The object that is bound to your Javascript runs in another thread and not in the thread in which it was constructed.

    允许网页调用android功能可以存在风险,比如加载其他网页,默认做法是使用浏览器去加载外部其他网页;
  2. 自定义的js对应andoird实现类

//通过webView按钮调用android toast功能
public class BasicJsAppInterface
private Context cxt;
public BasicJsAppInterface(Context cxt)
this.cxt &#61; cxt;

// 如果targetSDKVersion设置为17以上,这里需要添加该annotation标志
&#64;JavascriptInterface
public void showToast()
Toast.makeText(this.cxt, "toast in android", Toast.LENGTH_SHORT).show();

// 实现js调用android功能
WebView mWv &#61; (WebView) findViewById(R.id.wv);
WebSettings wvSettings &#61; mWv.getSettings();
wvSettings.setJavascriptEnabled(true);
wvSettings.setDefaultTextEncodingName("utf-8");
//传入实现js功能的android实例 以及 js调用时使用的名称
mWv.addJavascriptInterface(new BasicJsAppInterface(this), "AndroidApp");
//加载本地asset文件,以 &#96;file:///&#96; 开头
mWv.loadUrl("file:///android_asset/index.html");

1. js 调用 android 功能

// 在src/main/assets 目录(不存在则手动创建)中创建该html文件

<html lang&#61;"en">
<head>
<meta charset&#61;"UTF-8">
<title>webViewDemoFromAssettitle>
<script src&#61;"js/basic.js">script>
head>
<body>
<div>
<button id&#61;"btn" onclick&#61;&#39;showToast()&#39;>调用android toastbutton>
div>
<label id&#61;&#39;label&#39;>js android代码互调测试label>
<br>
<a href&#61;"http://lucid-lynxz.github.io/">github主页a>
<a href&#61;"http://www.jianshu.com/users/302253a7ed00/latest_articles">简书主页a>
<video width&#61;"400" controls>
<source src&#61;"res/shuai_dan_ge.mp4" type&#61;"video/mp4">
<source src&#61;"res/gongsi_de_liliang.flv" type&#61;"video/flv">
<p>不支持该格式视频p>
video>
body>
html>

注意

:这里引入的独立js文件标签不能简写成 &#96;

//在 assets/js/ 目录下创建js独立文件basic.js,当然也可以把这些代码直接嵌入到html中
function setLabel(id, label)
document.getElementById(id).innerHTML &#61; label;
function showToast()
AndroidApp.showToast(); //也可以写成window.AndroidApp.showToast();

2. android 调用js代码:

//前缀Javascript, &#96;setLabel()是网页js文件中定义的方法&#96;
mWv.loadUrl("Javascript:setLabel(&#39;label&#39;,&#39;通过android调用js代码&#39;)");

缓存/COOKIE

webview应用的缓存文件放置于 /data/data/yourProjectName/ 下面,之前想提取webview缓存的图片,往上查找的资料大都是通过 webviewcache.db 来获取图片对应的缓存文件,但是我在红米1s4.4以及nexus6p 6.0系统上都没有再发现这个文件了,新的缓存目录结构:

从上图可以发现 COOKIEs 文件存在,使用16进制编辑器打开查看,也可在程序中获取:

private String getCOOKIE()
COOKIEManager cm &#61; COOKIEManager.getInstance();
String COOKIE &#61; cm.getCOOKIE(mUrl);
if (TextUtils.isEmpty(COOKIE))
COOKIE &#61; "there is no COOKIE exist";

return COOKIE;

另外,图中红色方框内的文件就是缓存的文件了,它们名称是如何跟实际资源文件对应起来的,这个我还没弄懂,不过还是可以获取缓存图片的,我们使用16进制编辑器来查看,可以发现头部有该图片的url地址(“?g….d64d.png”):

我们删除该文件的url地址信息,保存后,修改后缀名为png,即可看到实际的图片:


播放视频

支持标准MP4,ogg之类的,flash得启用插件进行播放,不考虑 [官网](http://developer.android.com/intl/zh-cn/reference/android/webkit/WebView.html) 建议播放视频的时候开启硬件加速,不过我在nexus6p上没有开(默认开了吗?)也ok的;

全屏播放

[参考](http://blog.csdn.net/zrzlj/article/details/8050633)

页面适应

[Pixel-Perfect UI in the WebView](https://developer.chrome.com/multidevice/webview/pixelperfect)
  • 一个针对移动端优化过的页面带有如下类似的属性:

<meta name&#61;"viewport" content&#61;"width&#61;device-width, initial-scale&#61;1, maximum-scale&#61;1"> 系统将页面显示在一个虚拟的Viewport中,这个视口通常比屏幕大,这样网页就不会被限制在很小的范围内,用户可以通过缩放和平移来查看内容;

  • 对于无法控制内容的线上网页,可以通过代码方式设置ViewPort:

//强制手机使用 desktop-size viewport
wvSettings.setUseWideViewPort(true);
wvSettings.setLoadWithOverviewMode(true);

扩展-响应式


调试
  1. chrome
    需要在电脑上安装Chrome32以上的版本;

  2. 在电脑上启动浏览器打开网址 chrome://inspect ,

  3. android启用webView调试
    条件:


    • android 4.4以上
    • 允许远端调试

if (Build.VERSION.SDK_INT >&#61; Build.VERSION_CODES.KITKAT)
// 官网: https://developer.chrome.com/devtools/docs/remote-debugging#debugging-webviews
// 官网说WebView不受manifest的debuggable标签的影响,若需要在该标签启用时才允许调试,则添加如下条件判断(注意:尽量不要在manifest中显式指定debuggable属性,放空即可,这样Android Studio会自动在调试时设置成true,在release版本中设置成false)
int debuggable &#61; getApplicationInfo().flags &&#61; ApplicationInfo.FLAG_DEBUGGABLE;
if (0 !&#61; debuggable)
WebView.setWebContentsDebuggingEnabled(true);


注意事项
  1. All WebView methods must be called on the same thread
    wv.loadUrl() 方法放在主线程(根据错误提示来指定)中执行;
    P.S. webview类中有checkThread()方法,跟初始的Lopper.myLooper做比较,想跟踪setContentView看看,结果源码不全,断点跟踪不知道跟到哪里去了…后续得再补补;
  2. html页面应用独立的js文件时,script不能写成自闭合标签,否则浏览器解析可能会出错;
  3. 官方建议WebView的height属性设置为 match_parent 或者指定值,而非 wrap_content ,同事设置为 match_parent 后,其各个父容器不允许设置height为 wrap_content ,否则可能导致异常发生;
  4. android 4.4对webView做了些变化,可以参考 [这篇文章](Migrating to WebView in Android 4.4);
  5. 混淆时,需要设置JavascriptInterface不被混淆

# app/proguard-rules.pro
-keep public class org.lynxz.webviewdemo.BasicJsAppInterface
public <methods>;
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*

异常

1. 内存泄露

这个我还没测试,Android WebView Memory Leak WebView内存泄漏
&#61;&#61;! 然后查了内存检查:
Android最佳性能实践(二)——分析内存的使用情况

这里有人发现android 5.1也有类似的情况,我没有尝试加载很多页面,先记录下来:
Android 5.1 Webview 内存泄漏新场景


2. loadData() 中文乱码

参考这篇

mWv.loadData(yourHtmlString, "text/html;charset&#61;UTF-8", null);

有人说这么设置也可以避免乱码,但是我在nexus 6p上没测试成功:

wvSettings.setDefaultTextEncodingName("utf-8");

3. eglCodecCommon: ** ERROR unknown type 0x73000f (glSizeof,80)

Genymotion模拟器不支持硬件加速,关闭即可:
mWv.setLayerType(View.LAYER_TYPE_SOFTWARE, null);


4. html中含有angular.js,数据获取成功,但是显示空白:

已启用js支持:
mWv.getSettings().setJavascriptEnabled(true);

logCat报错:

I/xxx: url &#61; http://fep-web.debug.web.nd/#!/report/student/compositive?client&#61;phone&mode&#61;debug&user_id&#61;2079947956
D/dalvikvm: GC_FOR_ALLOC freed 37K, 14% free 21151K/24528K, paused 17ms, total 17ms
I/dalvikvm-heap: Grow heap (frag case) to 25.837MB for 3288976-byte allocation
W/AwContents: nativeOnDraw failed; clearing to background color.
I/Timeline: Timeline: Activity_idle id: android.os.BinderProxy&#64;425bdee8 time:31230137
I/chromium: [INFO:CONSOLE(39)] "Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.4.10/$injector/modulerr?p0&#61;app&p1&#61;Error%3A%20%5B%24injector%3Amodulerr%5D%20http%3A%2F%2Ferrors.angularjs.org%2F1.4.10%2F%24injector%2Fmodulerr%3Fp0%3Dapp-theme%26p1%3DTypeError%253......3)", source: http://fep-web.debug.web.nd/bower_components/angular/angular.min.js?v&#61;201604181940 (39)
I/chromium: [INFO:CONSOLE(72)] "error_log:localStorage error", source: http://fep-web.debug.web.nd/js-error.no-ng.js (72)

我也不懂angular.js用到了什么,添加下dom支持就可以了:

settings.setDomStorageEnabled(true);

5. A WebView method was called on thread ‘JavaBridge’. All WebView methods must be called on the same thread.

webview加载了网页后,在html中点击重新加载网页,我之前直接在接口类的方法中直接运行,

&#64;JavascriptInterface
public void retriveToUrl(String url)
mWv.loadUrl(url);

需要将其放置在ui线程中运行:



The Javascript method is executed on a background (i.e. non-UI) thread. You need to call all Android View related methods on the UI thread.


&#64;JavascriptInterface
public void retriveToUrl(String url)
mWv.post(new Runnable()
&#64;Override
public void run()
mWebView.loadUrl(...).

);

6. “TypeError: Object [object Object] has no method ‘callNative’ - 混淆

在release版本中,js代码调用不到我定义的接口类中的方法,从混淆文件中把这个类排除即可;
js interface proguard


推荐阅读
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了OkHttp3的基本使用和特性,包括支持HTTP/2、连接池、GZIP压缩、缓存等功能。同时还提到了OkHttp3的适用平台和源码阅读计划。文章还介绍了OkHttp3的请求/响应API的设计和使用方式,包括阻塞式的同步请求和带回调的异步请求。 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • 最简便的 JavaScript 代码检查工具安装方式
    前两天发了一篇用mingw编译javascriptv8,讲述我为了在Windows下给javascript做lint检查,费劲去编译google的jav ... [详细]
  • 浏览器如何工作(How browsers work)的阅读笔记
    浏览器如何工作(Howbrowserswork)的阅读笔记1.整体结构完整的浏览器整体框架的发改如下:UI:就是那些我们常常 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了预加载多个本地WebView相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 唱唱反调:风口上的技术不要盲目追
      对于前端领域的开发者来说,“学不动了”虽然更多是一种调侃,但也真实地反映出了他们面对频繁出新的前端技术时又爱又恨的心情。在经历了移动互联网的大爆发后,前端领域的边界不 ... [详细]
  • Python实现变声器功能(萝莉音御姐音)的方法及步骤
    本文介绍了使用Python实现变声器功能(萝莉音御姐音)的方法及步骤。首先登录百度AL开发平台,选择语音合成,创建应用并填写应用信息,获取Appid、API Key和Secret Key。然后安装pythonsdk,可以通过pip install baidu-aip或python setup.py install进行安装。最后,书写代码实现变声器功能,使用AipSpeech库进行语音合成,可以设置音量等参数。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • Asp.net Mvc Framework 七 (Filter及其执行顺序) 的应用示例
    本文介绍了在Asp.net Mvc中应用Filter功能进行登录判断、用户权限控制、输出缓存、防盗链、防蜘蛛、本地化设置等操作的示例,并解释了Filter的执行顺序。通过示例代码,详细说明了如何使用Filter来实现这些功能。 ... [详细]
  • v8对象机制1.概述v8中每一个API对象都对应一个内部实现对象(堆对象)2.对象创建过程(1)v8::internal::Factory类: ... [详细]
  • 技术周报·2021-05-07-小编推荐向现代Javascript转型原文标题:Publish,ship,andinstallmodernJavaScriptforfaste ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有